﻿using System;
using System.Collections;
using System.Collections.Generic;

namespace Rules
{
    public class RuleCollection : IEnumerable<IRule>
    {
        public RuleCollection(IEnumerable<IRule> rules)
        {
            Rules = new List<IRule>(rules);
        }

        private List<IRule> Rules { get; set; }

        IEnumerator<IRule> IEnumerable<IRule>.GetEnumerator()
        {
            return Rules.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return (Rules as IEnumerable).GetEnumerator();
        }

        public bool IsEmpty
        {
            get { return Rules.Count == 0; }
        }

        public bool IsValid
        {
            get { return Find(false).IsEmpty; }
        }

        public bool IsPropertyValid(string propertyName)
        {
            return Find(false, propertyName).IsEmpty;
        }

        public RuleCollection Find(bool isValid)
        {
            IEnumerable<IRule> rules = Rules.FindAll(delegate(IRule rule)
            {
                return rule.IsValid == isValid;
            });
            return new RuleCollection(rules);
        }

        public RuleCollection Find(string propertyName)
        {
            IEnumerable<IRule> rules = Rules.FindAll(delegate(IRule rule)
            {
                return rule.PropertyName == propertyName;
            });
            return new RuleCollection(rules);
        }

        public RuleCollection Find(bool isValid, string propertyName)
        {
            IEnumerable<IRule> rules = Rules.FindAll(delegate(IRule rule)
            {
                return rule.IsValid == isValid && rule.PropertyName == propertyName;
            });
            return new RuleCollection(rules);
        }

        public RuleCollection Evaluate<T>(T subject)
        {
            if (subject == null)
            {
                throw new ArgumentNullException("subject");
            }
            foreach (IRule rule in Rules)
            {
                EvaluateRule<T>(rule, subject);
            }
            return Find(false);
        }

        private void EvaluateRule<T>(IRule rule, T subject)
        {
            PropertyHelper<T> helper = new PropertyHelper<T>(rule.PropertyName);
            helper.ValidateProperty();
            rule.Evaluate(helper.GetPropertyValue(subject));
        }
    }
}
